package xyz.scootaloo.kami.cloud.service.impl

import xyz.scootaloo.kami.cloud.lang.Global
import xyz.scootaloo.kami.cloud.lang.currentTimeMillis
import xyz.scootaloo.kami.cloud.lang.getLogger
import xyz.scootaloo.kami.cloud.service.CrontabService
import xyz.scootaloo.kami.cloud.service.CrontabService.Crontab
import xyz.scootaloo.kami.cloud.service.SingletonCrontab
import xyz.scootaloo.kami.cloud.service.ServiceLifeCycle
import java.util.TreeMap

/**
 * @author flutterdash@qq.com
 * @since 2022/4/10 21:38
 */
object CrontabServiceImpl : CrontabService, ServiceLifeCycle {

    private val log by lazy { getLogger("crontab-service") }

    private val taskQue = TreeMap<String, CrontabWrapper>()

    override fun onAppStarted() {
        SingletonCrontab.singletons().forEach(::submit)
        Global.VERTX_REF.setPeriodic(Crontab.defDelay) {
            runTaskQue()
        }
    }

    override fun onDatabaseAvailable() {

    }

    override fun submit(crontab: Crontab) {
        val wrapper = CrontabWrapper.of(crontab)
        taskQue[wrapper.id] = wrapper
    }

    private fun runTaskQue() {
        val currentTimeMillis = currentTimeMillis()
        val invalidTaskIds = ArrayList<String>(0)
        for ((id, wrapper) in taskQue) {
            if (!checkAndRun(currentTimeMillis, wrapper)) {
                invalidTaskIds.add(id)
            }
        }

        if (invalidTaskIds.isNotEmpty()) {
            for (id in invalidTaskIds) {
                taskQue.remove(id)
            }
        }
    }

    private fun checkAndRun(currentTimeMillis: Long, wrapper: CrontabWrapper): Boolean {
        val crontab = wrapper.crontab
        val interval = currentTimeMillis - wrapper.lastExecuteTime
        if (interval > crontab.delay) {
            try {
                wrapper.lastExecuteTime = currentTimeMillis
                crontab.run(currentTimeMillis)
            } catch (err: Throwable) {
                log.error("定时任务执行出错, name:${crontab.name}, msg: ${err.message}", err)
            }
        }
        return crontab.valid
    }

    class CrontabWrapper(
        var lastExecuteTime: Long,
        val id: String,
        val crontab: Crontab
    ) {
        companion object {
            fun of(crontab: Crontab) = CrontabWrapper(
                currentTimeMillis(),
                generateId(crontab),
                crontab
            )

            private fun generateId(crontab: Crontab): String {
                return "${crontab.order}${crontab.name}"
            }
        }
    }
}